package cn.tedu.mall.seckill.service.impl;

import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.pojo.domain.CsmallAuthenticationInfo;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.order.service.IOmsOrderService;
import cn.tedu.mall.pojo.order.dto.OrderAddDTO;
import cn.tedu.mall.pojo.order.dto.OrderItemAddDTO;
import cn.tedu.mall.pojo.order.vo.OrderAddVO;
import cn.tedu.mall.pojo.seckill.dto.SeckillOrderAddDTO;
import cn.tedu.mall.pojo.seckill.model.Success;
import cn.tedu.mall.pojo.seckill.vo.SeckillCommitVO;
import cn.tedu.mall.seckill.config.RabbitMqComponentConfiguration;
import cn.tedu.mall.seckill.service.ISeckillService;
import cn.tedu.mall.seckill.utils.SeckillCacheUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
@Slf4j
public class SeckillServiceImpl implements ISeckillService {

    // 秒杀业务中,一定会使用到Redis,要装配操作Redis的对象
    // 因为库存数是使用StringRedisTemplate保存的,所以操作也需要这个对象
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    // 需要dubbo调用mall_order模块生成订单的业务
    @DubboReference
    private IOmsOrderService dubboOrderService;
    // 业务要求将秒杀成功信息保存到消息队列
    @Autowired
    private RabbitTemplate rabbitTemplate;

    /*
    1.判断用户是否为重复购买和Redis中该Sku是否有库存
    2.秒杀订单转换成普通订单,需要使用dubbo调用order模块的生成订单方法
    3.使用消息队列(RabbitMQ)将秒杀成功记录信息保存到success表中
    4.秒杀订单信息返回
     */

    @Override
    public SeckillCommitVO commitSeckill(SeckillOrderAddDTO seckillOrderAddDTO) {
        // 第一部分:判断用户是否为重复购买和Redis中该Sku是否有库存
        // 从参数中,获取用户购买商品的skuId
        Long skuId = seckillOrderAddDTO.getSeckillOrderItemAddDTO().getSkuId();
        Long userId = getUserId();
        String reseckillCheckKey = SeckillCacheUtils.getReseckillCheckKey(skuId, userId);
        Long boughtTimes = stringRedisTemplate.boundValueOps(reseckillCheckKey).increment();
        if (boughtTimes > 1) {
            throw new CoolSharkServiceException(ResponseCode.FORBIDDEN,
                    "抱歉，该秒杀商品最对只能买一件哦");
        }
        String skuStockKey = SeckillCacheUtils.getStockKey(skuId);
        if (!stringRedisTemplate.hasKey(skuStockKey)) {
            throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR, "缓存中没有库存信息，购买失败！");
        }
        Long leftStock = stringRedisTemplate.boundValueOps(skuStockKey).decrement();
        if (leftStock < 0) {
            stringRedisTemplate.boundValueOps(reseckillCheckKey).decrement();
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST, "抱歉，商品已售馨!");
        }

        // 到此为止,当前用户通过了重复购买的检查,并且库存还有剩余,可以开始生成订单了
        // 第二部分: 秒杀订单转换成普通订单,需要使用dubbo调用order模块的生成订单方法
        // 当前方法参数SeckillOrderAddDTO,需要转换为dubbo调用使用的OrderAddDTO
        // 下面进行转换,转换过程的代码编写在一个单独的方法中
        OrderAddDTO orderAddDTO = convertSeckillOrderToOrder(seckillOrderAddDTO);
        orderAddDTO.setUserId(userId);
        OrderAddVO orderAddVO = dubboOrderService.addOrder(orderAddDTO);

        Success success = new Success();
        BeanUtils.copyProperties(seckillOrderAddDTO.getSeckillOrderItemAddDTO(),success);
        success.setUserId(userId);
        success.setSeckillPrice(seckillOrderAddDTO.getSeckillOrderItemAddDTO().getPrice());
        success.setOrderSn(orderAddVO.getSn());
        rabbitTemplate.convertAndSend(
                RabbitMqComponentConfiguration.SECKILL_EX,
                RabbitMqComponentConfiguration.SECKILL_RK,
                success);

        SeckillCommitVO commitVO = new SeckillCommitVO();
        BeanUtils.copyProperties(orderAddVO,commitVO);
        return commitVO;
    }

    private OrderAddDTO convertSeckillOrderToOrder(SeckillOrderAddDTO seckillOrderAddDTO) {
        OrderAddDTO orderAddDTO = new OrderAddDTO();
        BeanUtils.copyProperties(seckillOrderAddDTO,orderAddDTO);

        OrderItemAddDTO orderItemAddDTO = new OrderItemAddDTO();
        BeanUtils.copyProperties(seckillOrderAddDTO.getSeckillOrderItemAddDTO(),orderItemAddDTO);

        List<OrderItemAddDTO> list = new ArrayList<>();
        list.add(orderItemAddDTO);
        orderAddDTO.setOrderItems(list);
        return orderAddDTO;
    }


    public CsmallAuthenticationInfo getUserInfo() {
        // 获取SpringSecurity上下文
        UsernamePasswordAuthenticationToken authenticationToken =
                (UsernamePasswordAuthenticationToken)
                        SecurityContextHolder.getContext().getAuthentication();
        // 为了逻辑更加严谨,判断一下SpringSecurity上下文内容是否为null
        if (authenticationToken == null) {
            throw new CoolSharkServiceException(
                    ResponseCode.UNAUTHORIZED, "您还未登录");
        }
        // 从SpringSecurity上下文中获取用户信息
        CsmallAuthenticationInfo csmallAuthenticationInfo =
                (CsmallAuthenticationInfo) authenticationToken.getCredentials();
        // 最终返回用户信息
        return csmallAuthenticationInfo;
    }

    // 业务逻辑层实际开发时,大部分都是需要用户的Id而已
    // 所以我们再编写一个方法,只返回用户的Id
    public Long getUserId() {
        return getUserInfo().getId();
    }

}
